﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ReversiBackend;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.IO;
using ReversiFrontend;
using System.Resources;
using ReversiFrontend.Properties;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ReversiFrontend
{
    public delegate void NewGameCallback();

    public partial class GUI : Form{
    
        #region user32 dll imports (for borderless window operations)
        public const int WM_NCLBUTTONDOWN = 0xA1;
        public const int HT_CAPTION = 0x2;
        public const int HTBOTTOMRIGHT = 17;

        [DllImportAttribute("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd,
                         int Msg, int wParam, int lParam);
        [DllImportAttribute("user32.dll")]
        public static extern bool ReleaseCapture();
#endregion

        private MyMessageBox mbox;
        // time to wait (in ms) until AI will do a move
        private const int AIDELAY = 1000;

        private Game game;


        public GUI()
        {
            InitializeComponent();
            game = new Game();
            game.CurrentPlayerChanged += new Game.CurrentPlayerChangedHandler(game_CurrentPlayerChanged);
            game.CurrentPlayerChanged += new Game.CurrentPlayerChangedHandler(boardControl1.SetCurrentPlayer);
            game.GameOver += new GameOverHandler(game_GameOver);
            boardControl1.Board = game.Board;
            // zum Testen:
            //Player player1 = new Human(game, -1, boardControl1);
            //Player player2 = new Human(game, 1, boardControl1);
            //game.NewGame(player1, player2);

            // set default values for "type"-combo boxes in "new game" tab
            playerType1.SelectedIndex = 0;
            playerType2.SelectedIndex = 1;

            //MyPanel.BaseColor = System.Drawing.SystemColors.ActiveCaption;
            styleComboBox.SelectedIndex = 0;
            stoneComboBox.SelectedIndex = 0;

            // verknüpfe tab panels mit zugehörigen buttons
            newGameTab.Tag = newGameTabButton;
            aboutTab.Tag = aboutTabButton;
            settingsTab.Tag = settingsTabButton;

            // verstecke player-infos bis zum ersten spiel
            cellControl1.Hide();
            cellControl2.Hide();
            playerInfo1.Hide();
            playerInfo2.Hide();

            // starte mit standard layout
            switchButtonLayout("standard");

            // zeige beim Start den "NeuesSpiel" Tab, ohne Wechselanimation
            showTab(newGameTab, false);

            // falls es noch keinen speicherordner gibt, erzeuge einen
            if (!Directory.Exists("savegame"))
            {
                Directory.CreateDirectory("savegame");
            }

            // load helptext into helpBox
            helpBox.Rtf = Properties.Resources.helptext;
        }

        void game_GameOver(int winner)
        {
            string s = "";
            if (winner == -1) {
                playerInfo1.Highlight = HighlightMode.Winner;
                playerInfo2.Highlight = HighlightMode.Loser;
                s = playerInfo1.PlayerName + " gewinnt!";
            }
            else if (winner == 1)
            {
                playerInfo1.Highlight = HighlightMode.Loser;
                playerInfo2.Highlight = HighlightMode.Winner;
                s = playerInfo2.PlayerName + " gewinnt!";
            }
            else //if (winner == 0)
            {
                playerInfo1.Highlight = HighlightMode.Neutral;
                playerInfo2.Highlight = HighlightMode.Neutral;
                s = "Es ist unentshieden!";
            }
            playerInfo1.Count = game.Board.P1Count;
            playerInfo2.Count = game.Board.P2Count;
            s += "\n" + playerInfo1.Count + " zu " + playerInfo2.Count + ".";
            MyMessageBox winbox = new MyMessageBox(s, false);
            winbox.ShowDialog();
        }

        void game_CurrentPlayerChanged(int newCurrentPlayer)
        {
            if (newCurrentPlayer == -1)
            {
                playerInfo1.Highlight = HighlightMode.ActivePlayer;
                playerInfo2.Highlight = HighlightMode.NextPlayer;
            }
            else //if (newCurrentPlayer == 1)
            {
                playerInfo2.Highlight = HighlightMode.ActivePlayer;
                playerInfo1.Highlight = HighlightMode.NextPlayer;
            }
            playerInfo1.Count = game.Board.P1Count;
            playerInfo2.Count = game.Board.P2Count;   
        }



        private void TitleBar_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                ReleaseCapture();
                SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
            }
        }

        private void titlebarCloseButton_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void resizeButton_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                ReleaseCapture();
                SendMessage(Handle, WM_NCLBUTTONDOWN, HTBOTTOMRIGHT, 0);
            }
            //Control c = this;
            //int heightDiff = c.Height - statHeight;
            //int widthDiff = c.Width - statWidth;

            //c.Height += widthDiff;
            //c.Width += heightDiff;
        }

        private void showTab(Panel tab, bool animate=true)
        {
            if (animate)
            {
                // only show hidden tabs
                if (!tab.Visible)
                {

                    extraContentBox.Collapse(() =>
                    {
                        foreach (Panel p in extraContentBox.Controls.OfType<Panel>())
                        {
                            p.Visible = p == tab;
                        }
                        extraContentBox.Expand(() => { });
                        AdjustTabHighlighter(tab);
                    });
                }
            }
            else
            {
                // without animation
                foreach (Panel p in extraContentBox.Controls.OfType<Panel>())
                {
                    p.Visible = p == tab;
                }
                AdjustTabHighlighter(tab);
            }
        }

        private void AdjustTabHighlighter(Panel tab)
        {
            var oldTab = tabHighlighter.Tag as ImageButton;
            if (oldTab != null)
            {
                tabHighlighter.Controls.Remove(oldTab);
                infoPanel.Controls.Add(oldTab);
                oldTab.BringToFront();
                oldTab.Location = tabHighlighter.Location;
            }
            var newTab = tab.Tag as ImageButton;
            if (newTab != null)
            {
                infoPanel.Controls.Remove(newTab);
                tabHighlighter.Controls.Add(newTab);
                tabHighlighter.Location = newTab.Location;
                tabHighlighter.Size = newTab.Size;
                newTab.Location = new Point(0, 0);
                tabHighlighter.Tag = newTab;
            }
        }


        private void newGameTabButton_Click(object sender, EventArgs e)
        {
            showTab(newGameTab);
        }

        private void settingsTabButton_Click(object sender, EventArgs e)
        {
            showTab(settingsTab);
        }

        private void aboutTabButton_Click(object sender, EventArgs e)
        {
            showTab(aboutTab);
        }


        private void showHintsCB_CheckedChanged(object sender, EventArgs e)
        {
            boardControl1.ShowValidFields = showHintsCB.Checked;
        }

        private void playerType1_SelectedIndexChanged(object sender, EventArgs e)
        {
            playerName1.Enabled = playerType1.SelectedItem.ToString() == "Mensch";
            switch (playerType1.SelectedItem.ToString())
            {
                case "Mensch":
                    playerName1.Text = "Spieler1";
                    break;
                case "PC (einfach)":
                    playerName1.Text = "easy Egon";
                    break;
                case "PC (schwierig)":
                    playerName1.Text = "hardcore Heiner";
                    break;
                default:
                    throw new Exception("Unbekannter Spielertyp");
            }
        }

        private void playerType2_SelectedIndexChanged(object sender, EventArgs e)
        {
            playerName2.Enabled = playerType2.SelectedItem.ToString() == "Mensch";
            switch (playerType2.SelectedItem.ToString())
            {
                case "Mensch":
                    playerName2.Text = "Spieler2";
                    break;
                case "PC (einfach)":
                    playerName2.Text = "easy Egon";
                    break;
                case "PC (schwierig)":
                    playerName2.Text = "hardcore Heiner";
                    break;
                default:
                    throw new Exception("Unbekannter Spielertyp");
            }
        }

        private void playButton_Click(object sender, EventArgs e)
        {
            startupFillPanel.Hide();
            if (game.Running) //TODO: und nicht grade gespeichert
            {
                mbox = new MyMessageBox( "Es läuft bereits ein Spiel. \r\n Möchten sie dieses wirklich beenden?", true);
                mbox.ShowDialog();
                DialogResult res = mbox.DialogResult;
                if (res == DialogResult.Cancel)
                {
                    return;
                }               
            }

            cellControl1.Show();
            cellControl2.Show();
            playerInfo1.Show();
            playerInfo2.Show();

            playerInfo1.PlayerName = playerName1.Text != "" ? playerName1.Text : "Spieler1";
            playerInfo2.PlayerName = playerName2.Text != "" ? playerName2.Text : "Spieler2";
            playerInfo1.IsHuman = playerType1.SelectedItem.ToString() == "Mensch";
            playerInfo2.IsHuman = playerType2.SelectedItem.ToString() == "Mensch";
            game.NewGame(StringToPlayer(playerType1.SelectedItem.ToString(), -1),
                         StringToPlayer(playerType2.SelectedItem.ToString(), 1));
        }

        private Player StringToPlayer(string name, int index)
        {
            switch (name){
                case "Mensch":
                    return new Human(game, index, boardControl1);
                case "PC (einfach)":
                    return new EasyAI(game, index, AITimerFunc);
                case "PC (schwierig)":
                    return new ComplexAI(game, index, AITimerFunc);
                default:
                    throw new NotImplementedException("Unbekannter Spielertyp: " + name);
            }
        }

        /// <summary>
        /// Calls the given Callback after a delay of AIDELAY milliseconds.
        /// </summary>
        /// <param name="c">The callback.</param>
        private void AITimerFunc(ReversiBackend.Callback c)
        {
            Timer t = new Timer();
            t.Interval = AIDELAY;
            t.Tick += (sender, args) => { c(); t.Stop(); };
            t.Enabled = true;
        }

        private void screenshotTabButton_Click(object sender, EventArgs e)
        {

            Bitmap b = new Bitmap(boardControl1.Width, boardControl1.Height);
            boardControl1.DrawToBitmap(b, new Rectangle(new Point(0,0), boardControl1.Size));

            if (!Directory.Exists("screenshots"))
            {
                Directory.CreateDirectory("screenshots");
            }
            string filename = string.Format("screenshots\\yarc-{0:dd-MM-yyyy_HH-mm-ss}.png", DateTime.Now);
            b.Save(filename);
            //TODO: give user feedback that we really took a screenshot
            mbox = new MyMessageBox("Screenshot gespeichert.", false);
            mbox.ShowDialog();
        }

        private void loadTabButton_Click(object sender, EventArgs e)
        {
            var diag = new OpenFileDialog();
            diag.DefaultExt = "yarc";
            diag.Filter = "yarc Spielstände|*.yarc";
            diag.InitialDirectory = Path.GetFullPath("savegame");
            diag.Title = "yarc - Spielladen...";
            var result = diag.ShowDialog(this);
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                LoadGame(diag.FileName);
            }
        }

        private void saveTabButton_Click(object sender, EventArgs e)
        {
            if (!game.Running)
            {
                mbox = new MyMessageBox("Spiel vorbei, kann nicht speichern!", false);
                mbox.ShowDialog();
                return;
            }
            var diag = new SaveFileDialog();
            diag.DefaultExt = "yarc";
            diag.Filter = "yarc Spielstände|*.yarc";
            diag.InitialDirectory = Path.GetFullPath("savegame");
            diag.Title = "yarc - Spiel speichern...";
            var result = diag.ShowDialog(this);
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                SaveGame(diag.FileName);
            }
        }

        private void Open_Link(object sender, LinkLabelLinkClickedEventArgs e)
        {
            string url = (sender as LinkLabel).Tag.ToString();
            // following snippet taken from http://www.devtoolshed.com/content/launch-url-default-browser-using-c
            // to work around some bugs
            try
            {

                System.Diagnostics.Process.Start(url);

            }

            catch (Exception exc1)
            {

                // System.ComponentModel.Win32Exception is a known exception that occurs when Firefox is default browser.  

                // It actually opens the browser but STILL throws this exception so we can just ignore it.  If not this exception,

                // then attempt to open the URL in IE instead.

                if (exc1.GetType().ToString() != "System.ComponentModel.Win32Exception")
                {

                    // sometimes throws exception so we have to just ignore

                    // this is a common .NET bug that no one online really has a great reason for so now we just need to try to open

                    // the URL using IE if we can.

                    try
                    {

                        System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo("IExplore.exe", url);

                        System.Diagnostics.Process.Start(startInfo);

                        startInfo = null;

                    }

                    catch (Exception exc2)
                    {
                        mbox = new MyMessageBox("Kann Link nicht öffnen: " + url, false);
                        mbox.ShowDialog();
                    }

                }

            }

        }

        private void playerName_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                playButton_Click(sender, null);
            }
        }

        private void aiCalcDeepNUD_ValueChanged(object sender, EventArgs e)
        {
            ComplexAI.Deep = (int) aiCalcDeepNUD.Value;
        }

        private ResourceManager rm;
        private void switchButtonLayout(string style)
        {
            rm = new ResourceManager("ReversiFrontend.Properties.Resources", typeof(Resources).Assembly);
            this.closeButton.BackgroundImage = (Image)rm.GetObject(String.Format("{0}_close", style));
            this.closeHelpButton.BackgroundImage = (Image)rm.GetObject(String.Format("{0}_close", style));
            this.aboutTabButton.BackgroundImage = (Image)rm.GetObject(String.Format("{0}_about", style));
            this.minButton.BackgroundImage = (Image)rm.GetObject(String.Format("{0}_min", style));
            this.helpTabButton.BackgroundImage = (Image)rm.GetObject(String.Format("{0}_help", style));
            //this.okButton.BackgroundImage = (Image)rm.GetObject(String.Format("{0}_close", style));
        }

        private void styleComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (styleComboBox.Text)
            {
                case "default": MyPanel.BaseColor = System.Drawing.SystemColors.ActiveCaption;
                    boardControl1.BaseColor = Color.Brown;
                    CellControl.StoneLayout = StoneLayouts.Marble;
                    switchButtonLayout("standard");
                    break;
                case "dark": MyPanel.BaseColor = System.Drawing.Color.Black;
                    boardControl1.BaseColor = Color.DarkSlateGray;
                    CellControl.StoneLayout = StoneLayouts.Monster;
                    switchButtonLayout("dark");
                    break;
                case "light": MyPanel.BaseColor = System.Drawing.Color.White;
                    boardControl1.BaseColor = Color.Silver;
                    CellControl.StoneLayout = StoneLayouts.Default;
                    switchButtonLayout("standard");
                    break;
                case "hello kitty": MyPanel.BaseColor = System.Drawing.Color.DeepPink;
                    boardControl1.BaseColor = ControlPaint.Light(Color.DeepPink, 0.9f);
                    CellControl.StoneLayout = StoneLayouts.HelloKitty;
                    switchButtonLayout("hk");
                    break;
            }
        }

        private void stoneComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (stoneComboBox.Text == "classic")
            {
                CellControl.Animated = false;
            }
            else
            {
                CellControl.Animated = true;
            }
        }

        #region Load/Save

        public void SaveGame(string path)
        {
           var data = new Dictionary<string, object>();
           Player p1, p2;
           game.DumpData(data, out p1, out p2);
           playerInfo1.DumpData(data, "1");
           playerInfo2.DumpData(data, "2");

           data.Add("player1", p1.Name);
           data.Add("player2", p2.Name);
          
         //  XmlSerializer ser = new XmlSerializer(data.GetType());
           BinaryFormatter formatter = new BinaryFormatter();
           FileStream str = new FileStream(path, FileMode.Create);
         //  ser.Serialize(str, data);
           formatter.Serialize(str, data);
           str.Close();


        }

        public void LoadGame(string path)
        {
            //XmlSerializer ser = new XmlSerializer(typeof(Dictionary<string, object>));
            BinaryFormatter formatter = new BinaryFormatter();
            StreamReader sr = new StreamReader(path);
            Dictionary<string, object> data = (Dictionary<string, object>)formatter.Deserialize(sr.BaseStream);
            sr.Close();

            game.LoadData(data, StringToPlayer((string)data["player1"], -1), StringToPlayer((string)data["player2"], 1));
            playerInfo1.LoadData(data, "1");
            playerInfo2.LoadData(data, "2");


        }
        #endregion

        private void minButton_Click(object sender, EventArgs e)
        {
            Timer t = new Timer();
            t.Interval = 50;
            t.Tick += (s, args) =>
            {
                if (this.Opacity > 0)
                {
                    this.Opacity -= 0.1;
                }
                else
                {
                    t.Stop();
                    this.WindowState = FormWindowState.Minimized;
                }
            };
            t.Start();
        }

        protected override void OnActivated(EventArgs e)
        {
            base.OnActivated(e);
            if (Opacity <= 0)
            {
                Timer t = new Timer();
                t.Interval = 50;
                t.Tick += (s, args) =>
                {
                    if (this.Opacity < 1)
                    {
                        this.Opacity += 0.1;
                    }
                    else
                    {
                        t.Stop();
                        this.WindowState = FormWindowState.Normal;
                        Opacity = 1;
                    }
                };
                t.Start();
            }
        }

        private void helpTabButton_Click(object sender, EventArgs e)
        {
            closeHelpButton.Show();
            closeHelpButton.BringToFront();
            helpBox.Show();
            boardControl1.Hide();
        }

        private void closeHelpButton_Click(object sender, EventArgs e)
        {
            closeHelpButton.Hide();
            helpBox.Hide();
            boardControl1.Show();
        }
        



    }
}
